package org.gap.wizards.world;

import java.awt.Point;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.gap.jseed.injection.annotation.Singleton;
import org.gap.wizards.pc.PlayerCharacter;
import org.gap.wizards.utils.Persistence;
import org.gap.wizards.utils.ValuesPersistence;
import org.gap.wizards.views.ViewPlatform;
import org.gap.wizards.world.actions.Factory;

@Singleton
public class World implements MapContainer {
	private HashMap<String, Location> maps;
	private HashMap<String, List<Transition>> oneWayAutoTransitions;
	private String mapId;
	private PlayerCharacter player;

	public World(PlayerCharacter player) {
		this.player = player;
		maps = new HashMap<String, Location>();
		oneWayAutoTransitions = new HashMap<String, List<Transition>>();
	}

	@Override
	public void addMap(String id, Location map) {
		maps.put(id, map);
	}

	@Override
	public void startWithMap(String id) {
		mapId = id;
		activeMap().setPlayerCharacter(player);
	}

	@Override
	public void move(Movement move) {
		activeMap().move(move);
		Point characterLocation = activeMap().getCharacterLocation();
		Transition transition = autoTransitionPoints(characterLocation);
		if (transition != null) {
			performTransition(transition.toMapId(), transition.toLocation);
		}
	}

	@Override
	public void perform(Factory action) {
		WorldAction worldAction = action.createWorldAction();
		worldAction.perform();
	}

	private Transition autoTransitionPoints(Point location) {
		if (!oneWayAutoTransitions.containsKey(mapId)) {
			return null;
		}
		for (Transition each : oneWayAutoTransitions.get(mapId)) {
			if (each.fromLocation.equals(location)) {
				return each;
			}
		}
		return null;
	}
	
	@Override
	public void performTransition(String newMapId, Point newLocation) {
		activeMap().setPlayerCharacter(null);
		activeMap().setCharacterLocation(null);
		mapId = newMapId;
		activeMap().setCharacterLocation(newLocation);
		activeMap().setPlayerCharacter(player);
	}

	@Override
	public Location activeMap() {
		return maps.get(mapId);
	}

	@Override
	public void draw(ViewPlatform console) {
		activeMap().update();
		activeMap().draw(console);
	}

	@Override
	public String mapId() {
		return mapId;
	}

	@Override
	public void addAutoTransition(String fromId, Point fromLocation, String toId, Point toLocation) {
		Transition transition = new Transition(fromLocation, toId, toLocation);
		if (!oneWayAutoTransitions.containsKey(fromId)) {
			oneWayAutoTransitions.put(fromId, new LinkedList<World.Transition>());
		}
		List<Transition> transitionPoints = oneWayAutoTransitions.get(fromId);
		transitionPoints.add(transition);
	}

	@Override
	public void persist(Persistence persister) throws IOException {
		persister.set("active.map", mapId);
		String[] locations = new String[maps.size()];
		int i = 0;
		for (String each : maps.keySet()) {
			Location location = maps.get(each);
			locations[i] = each + ";" + location.getClass().getName();
			Persistence locationPersistence = persister.newPersister(each);
			location.persist(locationPersistence);
			locationPersistence.save();
			i++;
		}
		persister.set("maps", locations);

		persister.set("transitions.from", oneWayAutoTransitions.keySet());
		persister.beginSection("auto.transitions");
		for (String eachMapId : oneWayAutoTransitions.keySet()) {
			persister.beginSection(eachMapId);
			{
				List<Transition> transitionsFrom = oneWayAutoTransitions.get(eachMapId);
				persister.set("count", transitionsFrom.size());
				i = 0;
				for (Transition each : transitionsFrom) {
					persister.set("transition." + i++, each.toString());
				}
			}
			persister.endSection();
		}
		persister.endSection();
		persister.save();
	}

	@Override
	public boolean load(Persistence persister) throws IOException {
		mapId = persister.getString("active.map");
		if (mapId != null) {
			String[] locations = persister.getStrings("maps");
			for (String each : locations) {
				String[] nameAndLocation = each.split(";");
				Location location = (Location) ValuesPersistence.readClass(nameAndLocation[1]);
				Persistence locationPersistence = persister.newPersister(nameAndLocation[0]);
				location.load(locationPersistence);
				addMap(nameAndLocation[0], location);
			}

			String[] mapKeys = persister.getStrings("transitions.from");
			persister.beginSection("auto.transitions");
			for (String eachMapId : mapKeys) {
				persister.beginSection(eachMapId);
				oneWayAutoTransitions.put(eachMapId, new LinkedList<World.Transition>());
				int transitionCount = persister.getInt("count");
				for (int i = 0; i < transitionCount; i++) {
					String transitionAsString = persister.getString("transition." + i);
					Transition transition = Transition.fromPoint(transitionAsString);
					oneWayAutoTransitions.get(eachMapId).add(transition);
				}
				persister.endSection();
			}
			persister.endSection();
			return true;
		}
		return false;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj.getClass().equals(World.class)) {
			World other = (World) obj;
			return mapId.equals(other.mapId) && oneWayAutoTransitions.size() == other.oneWayAutoTransitions.size();
		}
		return false;
	}

	public static class Transition {
		private String toId;
		private Point toLocation;
		private Point fromLocation;

		public Transition(Point fromLocation, String toId, Point toLocation) {
			this.fromLocation = fromLocation;
			this.toId = toId;
			this.toLocation = toLocation;
		}

		public String toMapId() {
			return toId;
		}

		public Point toLocation() {
			return toLocation;
		}

		public Point fromLocation() {
			return fromLocation;
		}
		
		@Override
		public String toString() {
			return asString(fromLocation) + ";" +
					toId + ";" +
					asString(toLocation);
		}

		private String asString(Point toLocation2) {
			return fromLocation.x + "," + fromLocation.y;
		}
		
		public static Transition fromPoint(String transition) {
			String[] data = transition.split(";");
			Point fromLocation = asPoint(data[0]);
			String toId = data[1];
			Point toLocation = asPoint(data[2]);
			return new Transition(fromLocation, toId, toLocation);
		}

		private static Point asPoint(String string) {
			String[] values = string.split(",");
			int x = Integer.parseInt(values[0]);
			int y = Integer.parseInt(values[1]);
			return new Point(x, y);
		}
	}
}
